﻿using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length < 1)
            {
                Console.WriteLine("Must input target directory!");
                return;
            }
            string dirPath = args[0];
            Console.WriteLine("the path:" + dirPath);
            DirectoryInfo dir = new(dirPath);
            GetDirs(dir);
            DataTable dt = new();

            foreach (string filePath in allExe)
            {
                Dictionary<string, List<string>> pairs = GetAllFunc(GetDumpString(filePath));
                SendInDataTable(dt, pairs);
            }
            Console.WriteLine("Will write data to Excel...");
            if (File.Exists("test.xlsx"))
                File.Delete("test.xlsx");
            TableToExcel(dt, "test.xlsx");
            Console.WriteLine("all data write to test.xlsx");
        }
        static readonly List<string> allExe = new();
        static void GetAllExecute(DirectoryInfo dirPath)
        {
            if (!dirPath.Exists)
            {
                return;
            }
            foreach (FileInfo file in dirPath.GetFiles())
            {
                if (file.Extension == ".exe" || file.Extension == ".dll" ||
                    file.Extension == ".EXE" || file.Extension == ".DLL")
                {
                    allExe.Add(file.FullName);
                }
            }
        }

        static void GetDirs(DirectoryInfo dirPath)
        {
            if (!dirPath.Exists)
            {
                Console.WriteLine("the path: " + dirPath.FullName + " not exist!");
                return;
            }
            GetAllExecute(dirPath);
            foreach (DirectoryInfo dir in dirPath.GetDirectories())
            {
                GetDirs(dir);
            }
        }

        static Dictionary<string, List<string>> GetAllFunc(string str)
        {
            Dictionary<string, List<string>> pairs = new();
            string[] strs = str.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
            List<string> result = new();
            bool head = false;
            List<string> filterStr = new();
            filterStr.Add("Characteristics");
            filterStr.Add("Address of HMODULE");
            filterStr.Add("Import Address Table");
            filterStr.Add("Import Name Table");
            filterStr.Add("Bound Import Name Table");
            filterStr.Add("Unload Import Name Table");
            filterStr.Add("date stamp");
            filterStr.Add("Index of first forwarder reference");
            foreach (string tmp in strs)
            {
                string t = tmp.Trim();
                #region 去除首尾无用字符
                if (!head)
                {
                    if (!t.Contains("Section contains the following imports"))
                    {
                        continue;
                    } else
                    {
                        head = true;
                    }
                }
                if (t.Contains("Summary"))
                {
                    break;
                }
                #endregion

                /*
                 加载动态库分为两部分：
                ‘Section contains the following imports’节表示先加载部分
                ‘Section contains the following delay load imports’节表示延迟加载部分
                 */

                if (t.Contains("Section contains the following imports"))
                {
                    continue;
                }
                if (t.Contains("Section contains the following delay load imports"))
                {
                    continue;
                }
                bool isFilter = false;
                //去除ddl与函数间无用的字符
                foreach (string filter in filterStr)
                {
                    if (t.Contains(filter)) {
                        isFilter = true;
                        break;
                    }
                }
                if (isFilter)
                    continue;
                t = t.Split(' ').Last();
                //Console.WriteLine(t);
                result.Add(t);
            }

            string dllName = "";
            bool isContinue = false;
            List<string> dllFuncs = new();
            foreach (string tmp in result)
            {
                if (isContinue)
                    continue;
                if (tmp.Contains(".dll") || tmp.Contains(".DLL"))
                {
                    if (dllName.Length != 0 && dllFuncs.Count != 0)
                    {
                        if (pairs.ContainsKey(dllName))
                        {
                            pairs[dllName].AddRange(dllFuncs);
                        } else
                        {
                            pairs.Add(dllName, dllFuncs);
                        }
                        dllFuncs = new();
                    }
                    dllName = tmp.ToLower();
                    continue;
                }
                //去掉纯数字部分
                if (Regex.IsMatch(tmp, @"^[+-]?\d*$"))
                    continue;
                //当函数名存在未知符号时，不导入该dll
                if (tmp.Contains('?') || tmp.Contains('@'))
                {
                    dllName = "";
                    dllFuncs.Clear();
                    isContinue = true;
                }
                //Console.WriteLine(dllName + "\t" + tmp);
                dllFuncs.Add(tmp);
            }
            return pairs;
        }

        static string GetDumpString(string execute)
        {
            Console.WriteLine("Will dump file: " + execute);
            string arg = string.Format("-imports -nologo \"{0}\"", execute);
            Process process = new();
            process.StartInfo.FileName = "dumpbin.exe";
            process.StartInfo.Arguments = arg;
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.RedirectStandardOutput = true;
            process.StartInfo.RedirectStandardError = true;
            process.StartInfo.RedirectStandardInput = true;
            process.Start();
            return process.StandardOutput.ReadToEnd();
        }

        static void SendInDataTable(DataTable dt, Dictionary<string, List<string>> funcs)
        {
            foreach (KeyValuePair<string, List<string>> pair in funcs)
            {
                if (!dt.Columns.Contains(pair.Key))
                {
                    dt.Columns.Add(pair.Key);
                }
                foreach (string value in pair.Value)
                {
                    DataRow dr = dt.NewRow();
                    dr[pair.Key] = value;
                    dt.Rows.Add(dr);
                }
            }
        }

        static void TableToExcel(DataTable dt, string file)
        {
            IWorkbook workbook = new XSSFWorkbook();

            ISheet sheet = string.IsNullOrEmpty(dt.TableName) ? workbook.CreateSheet("sheet0") : workbook.CreateSheet(dt.TableName);
            //表头  
            IRow row = sheet.CreateRow(0);
            for (int i = 0; i < dt.Columns.Count; i++)
            {
                ICell cell = row.CreateCell(i);
                cell.SetCellValue(dt.Columns[i].ColumnName);
            }

            //数据  
            for (int i = 0; i < dt.Rows.Count; i++)
            {
                IRow row1 = sheet.CreateRow(i + 1);
                for (int j = 0; j < dt.Columns.Count; j++)
                {
                    string celldata = dt.Rows[i][j].ToString();
                    if (string.IsNullOrEmpty(celldata))
                        continue;
                    ICell cell = row1.CreateCell(j);
                    cell.SetCellValue(celldata);
                }
            }
            //转为字节数组  
            MemoryStream stream = new();
            workbook.Write(stream);
            var buf = stream.ToArray();

            //保存为Excel文件  
            using FileStream fs = new(file, FileMode.OpenOrCreate, FileAccess.Write);
            fs.Write(buf, 0, buf.Length);
            fs.Flush();

        }
    }
}
